﻿using System;
using System.Collections.Generic;
using System.Threading.Tasks.Dataflow;
using NLog;

namespace QuantBox
{
    public class DataRequestManager
    {
        private readonly ActionBlock<DataRequestInfo> _requestBlock;
        private readonly Dictionary<string, DataRequestTask> _tasks;
        private readonly DataCenterInfo _info;
        private readonly Logger _logger;

        private void NewTask(DataRequestInfo request)
        {
            var client = new DataCenterClient(_info, request.EnableCache, request.DataCachePath);
            var task = new DataRequestTask(request, client, _logger);
            task.Done += OnTaskDone;
            task.DataReady += OnDataReady;
            task.Failed += OnTaskFailed;
            _tasks.Add(request.RequestId, task);
            task.Start();
        }

        private void RequestProcess(DataRequestInfo info)
        {
            if (info.Action == DataRequestAction.DataRequest
                || info.Action == DataRequestAction.InstrumentRequest) {
                NewTask(info);
            }
            else {
                if (!_tasks.TryGetValue(info.RequestId, out var task)) {
                    return;
                }
                _tasks.Remove(info.RequestId);
                if (!task.Completed) {
                    task.Stop();
                    if (info.Action != DataRequestAction.CancelRequest) {
                        return;
                    }
                    RequestCancelled?.Invoke(task.Info);
                }
                else {
                    RequestCompleted?.Invoke(task.Info);
                }
            }
        }

        private void OnTaskFailed(object sender, ErrorInfo e)
        {
            var task = (DataRequestTask)sender;
            RequestFailed?.Invoke(task.Info, e.ErrorMsg);
        }

        private void OnDataReady(object sender, EventArgs eventArgs)
        {
            var task = (DataRequestTask)sender;
            DataReady?.Invoke(task.Info, task.Data!);
        }

        private void OnTaskDone(object sender, EventArgs eventArgs)
        {
            var task = (DataRequestTask)sender;
            _requestBlock.Post(new DataRequestInfo(task.Info.RequestId, DataRequestAction.RemoveRequest));
        }

        public DataRequestManager(DataCenterInfo info, Logger logger)
        {
            _info = info;
            _logger = logger;
            _tasks = new Dictionary<string, DataRequestTask>();
            _requestBlock = new ActionBlock<DataRequestInfo>(RequestProcess, DataflowHelper.SpscBlockOptions);
        }

        public void Post(DataRequestInfo request)
        {
            _requestBlock.Post(request);
        }

        public void CancelRequest(string requestId)
        {
            _requestBlock.Post(new DataRequestInfo(requestId, DataRequestAction.CancelRequest));
        }

        public void Close()
        {
            _requestBlock.Complete();
            _requestBlock.Completion.Wait();
            foreach (var task in _tasks.Values) {
                task.Stop();
            }
            _tasks.Clear();
        }

        public event Action<DataRequestInfo, object[]>? DataReady;
        public event Action<DataRequestInfo, string>? RequestFailed;
        public event Action<DataRequestInfo>? RequestCompleted;
        public event Action<DataRequestInfo>? RequestCancelled;
    }
}